/**
 * Auteur : Dominique GUERIN
 * dominiquephDOTguerinATgmailDOT..
 * dominiqueDOTguerinATinseeDOTfr(ance)  
 * Remerciements: � Keith Brown pour ses deux livres sur la s�curit� sous Windows et ses diff�rents articles
 *                 et � Michel Barnett de Microsoft pour ses deux articles 
 *                ".NET Remoting Authentication and Authorization Sample"  et le code qui les accompagne
 *  Le code est utilisable, modifiable et redistribuable � volont� sous la condition de ne pas supprimer ces 7 lignes.
 */ 

package fr.doume.jna.sspi;


//tomcat 5.5
//import org.apache.commons.logging.Log;
//import org.apache.commons.logging.LogFactory;

//tomcat 6
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;

import fr.doume.jna.sspi.SSPException;
import fr.doume.jna.sspi.TranslationRolesIntoSids;

import fr.doume.jna.authenticator.Parameters;
import fr.doume.jna.authenticator.AuthenticationContext;
import fr.doume.jna.sspi.AuthenticationUtil;
import fr.doume.jna.sspi.WindowsContext;

/**
* Authentication class. Call the methods of AUthenticationUtil
  */
public class SSPAuthentication
{
	private static Log log = LogFactory.getLog(SSPAuthentication.class);
	

  private AuthenticationUtil au;  
 
 	private boolean failed;
	
	private boolean isContextSet;
	
	private boolean existUserName;	
	private String username = null;
  /**
  * Name of the Security Service Provider(NTLM or Kerberos)
  */
  private String sspname = null;

  private boolean existSSP;

  /**
   * True if the method communicateWithNegoServer has been already called (If yes => NTLM)
   */
	private boolean already_called;
	
	/**
	* instance given to the constructor. This instance has the Sids of the roles of tomcat.
	*/ 
	private TranslationRolesIntoSids translationTomcatRolesiIntoSids;
	
	private AuthenticationContext authenticationContext;
	
	private Parameters param;
	
	private boolean[] groupsInAccessToken = null;
	//debug
	
  public SSPAuthentication(AuthenticationContext ac)
  {
		failed = false;
		isContextSet = false;
		existUserName = false;
		existSSP = false;
		already_called = false;
		authenticationContext = ac;
		translationTomcatRolesiIntoSids = ac.getTranslationRolesIntoSids();
		param = ac.getParameters();
		au = new AuthenticationUtil( authenticationContext );
		log.debug("*******************************************!!!New SSPAuthentification!!!");
	}
  public SSPAuthentication(AuthenticationContext ac, String remoteAddr)
  {
		failed = false;
		isContextSet = false;
		existUserName = false;
		existSSP = false;
		already_called = false;
		authenticationContext = ac;
		translationTomcatRolesiIntoSids = ac.getTranslationRolesIntoSids();
		param = ac.getParameters();
		au = new AuthenticationUtil( authenticationContext, remoteAddr );
		if (remoteAddr == null) log.debug("*******************************************!!!New SSPAuthentification!!!");
		else log.debug("*******************************************!!!New SSPAuthentification!!!: remote address" + remoteAddr);    
  }
	/**
	* Receives the bytes sent by the client et returns bytes to send to the client.
	* Calls the methods of AuthenticationUtil.
	*/ 
	public byte[] acceptSecContext( byte[] token)
	throws SSPException
	{
		if (isContextSet){
			throw new SSPException("The SSPI/GSSAPIcontext is already established ");
		}
		if(token == null){
      throw new SSPException("The array sent by the client is null");
    }
		if (token.length == 0){
			throw new SSPException("The length of the array sent by the client is 0");
		}
		if ( failed ){
				throw new SSPException("SSPAuthentification already in error");
		}	

		log.debug("before au.getCredential");
		byte[] token_to_send = null;
		try
		{
      //if (! already_called){
      //  if (log.isDebugEnabled()) log.debug("before au.getCredential");
      //  au.getCredential();
      //  if (log.isDebugEnabled()) log.debug("after au.getCredential");
      //}
      token_to_send = au.getContext( token );
      if (log.isDebugEnabled()) log.debug("after au.getContext");
      
      if (au.isContinueNedded())
      {
        already_called = true;
      }
      else if (au.isEstablished() )
      {
        log.debug("before username");
        
        username = au.getClientName();
        existUserName = true;
        sspname = au.getSSPName();
        existSSP = true;
       
        if (param.areGroupsInAd())
        {
          if (log.isDebugEnabled()) log.debug("before getSids");
          byte[][] sids = translationTomcatRolesiIntoSids.getSids();

          if (log.isDebugEnabled()) log.debug("before RolesInAccessToken");
          groupsInAccessToken = au.RolesInAccessToken( sids);
          if (log.isDebugEnabled()) log.debug("after RolesInAccessToken");
        }

        isContextSet = true;

      }
      else if (au.isFailed() )
      {
        log.debug("Authentication is failed");
        failed = true;
        throw new SSPException("Authenttication is failed");
      }
      else
      {
        log.debug("Authentication is failed !!!");
        failed = true;
        throw new SSPException("Authenttication is failed");
      }
		}
		catch(Exception e)
		{
      failed = true;
      if (log.isDebugEnabled()) log.debug(e.getClass().getName() + " " + e.getMessage());
      throw new SSPException(e.getMessage());
    }
    if (log.isDebugEnabled()) log.debug("end acceptSecContext");

    return token_to_send;
  }  
		
	public void login(String name, String domain, String password)
	throws SSPException
	{
    try
    {
      au.login(name, domain, password);
      existUserName = true;
      username = name;
      if ( au.isEstablished() )
      {
        isContextSet = true;
      }
      if ( au.isFailed() )
      {
        failed = true;
      }

      if (param.areGroupsInAd())
      {
        if (log.isDebugEnabled()) log.debug("before getSids");
        byte[][] sids = translationTomcatRolesiIntoSids.getSids();

        if (log.isDebugEnabled()) log.debug("before RolesInAccessToken");
        groupsInAccessToken = au.RolesInAccessToken( sids);
        if (log.isDebugEnabled()) log.debug("after RolesInAccessToken");
      }
     
    }
		catch(Exception e)
		{
      failed = true;
      isContextSet = false;
      if (log.isDebugEnabled()) log.debug(e.getClass().getName() + " " + e.getMessage());
      throw new SSPException(e.getMessage());
    }
    if (log.isDebugEnabled()) log.debug("end login");
    
	}	
	public void login(String name, String password)
	throws SSPException
	{
    if (name == null || name.length() == 0) throw new SSPException("username is null");

    int position = name.indexOf('\\');
    String domain = null;
    if (position != -1 && name.length() > position +1)
    {
      domain = name.substring(0, position);
      name = name.substring(position +1);
    }
    else domain = "";

    if (log.isDebugEnabled()) log.debug("name : " + name + " , domain : " + domain);

    login(name, domain, password);
	}

	/**
	* Is client authentification estabblished ?
	*/
	public boolean isEstablished()
	{
		if ( isContextSet ){
			return true;
		}else{
			return false;
		}
	}

	/**
	* Is client authentification failed ?
	*/	
	public boolean isFailed()
	{
    return ( failed );
	}
	
  /**
  * Returns the name of the client when the authentication is established
  */
	public String getUserName()	
	throws SSPException
	{
		if (existUserName){
			return username;
		}
		else{
			throw new SSPException("User's name is unknown");
		}
	}
  /** Returns the Security Service Provider used by SPNEGO (NTLM or Kerberos).
  * Only for log.
  */
	public String getSSP()	
	throws SSPException
	{
		if (existSSP)
		{
			return sspname;
		}
		else
		{
			throw new SSPException("The name of the Security Service Provider Interface is unknown");
		}
	}

  /**
  * Returns a array of bytes given by AuthenticationUtil. 
  */
	public boolean[] areRolesInAccessToken()
	{
		return groupsInAccessToken;
	}
	
	public boolean isDisposed()
	{
    if (au == null)
    {
     if (log.isDebugEnabled()) log.debug("AuthenticationUtil is null");
      return false;
    }
    else return au.isDisposed();
	}
	
	public WindowsContext getWindowsContext()
  {
    return au.getWindowsContext(); 
  }

  public void setWindowsContext(WindowsContext winctx)
  {
    au.setWindowsContext(winctx);
  }

	
}
